package re56.layer;

import re56.packet.Packet;

public class PhysicalLayer
{
	// in dBm
	private int sensitivity;
	
	// in dBm
	private int power;
	
	// in dB
	private double Rxlevel;
	
	protected RadioLayer radioLayerDL;
	protected RadioLayer radioLayerUL;
	
	protected MACLayer Mac;
	
	
	protected double[] SpectralEfficiencyForCqiIndex = {
	  0.15, 0.23, 0.38, 0.6, 0.88, 1.18,
	  1.48, 1.91, 2.41,
	  2.73, 3.32, 3.9, 4.52, 5.12, 5.55
	};
	
	
	public PhysicalLayer(MACLayer Mac, RadioLayer radioLayerDL, RadioLayer radioLayerUL)
	{
		this.Mac = Mac;
		this.radioLayerDL = radioLayerDL;
		this.radioLayerUL = radioLayerUL;
	}
	
	public void setSensitivity(int sensitivity)
	{
		this.sensitivity = sensitivity;
	}

	public int getSensitivity()
	{
		return sensitivity;
	}

	public void setPower(int power)
	{
		this.power = power;
	}

	public int getPower()
	{
		return power;
	}
	
	public RadioLayer getDLRadioLayer()
	{
		return radioLayerDL;
	}
	
	public RadioLayer getULRadioLayer()
	{
		return radioLayerUL;
	}
	
	public void send(Packet p)
	{
		
	}
	
	
	public void receive()
	{
		// this function is called on each sub-frame. (should be 1ms)
		// when called, it should check all our allocated Channels to read packets.
	}
	
	
	public void computeRxLevel()
	{
		Rxlevel = radioLayerDL.computeRxLevel(Mac.getDevice());
		double Interferences = radioLayerDL.computeInterferences(Mac.getDevice());
		
		System.out.println("Interferences: "+Math.round(Interferences)+"dBm");
		// then check if we are really able to receive this signal.
		// based on our sensitivity
		
		double Rxlevel_mW = Math.pow(10, Rxlevel/10.0);
		double Noise_mW = Math.pow(10, -110.0/10.0);
		double Interferences_mW = Math.pow(10, Interferences/10.0);
		
		double sinr_mW = Rxlevel_mW / (Interferences_mW + Noise_mW);
		
		// in dB and not dBm.
		double sinr = 10 * Math.log10(sinr_mW) - 30;
		
		if(Rxlevel >= sensitivity)
		{
			// sounds good.
			CreateCqiFeedbacks(sinr);
		}
	}
	
	
	public int GetCqiFromSpectralEfficiency(double spectral_efficiency)
	{
		int cqi = 1;
		
		// CQI value between 1 and 14, depending on the spectral efficiency.
		while (SpectralEfficiencyForCqiIndex[cqi] < spectral_efficiency && cqi < 14)
	    {
	    	cqi++;
	    }
		
		return cqi;
	}
	
	
	public void CreateCqiFeedbacks(double sinr)
	{
		/*
	       * Compute the spectral efficiency from the SINR
	       *                                        SINR
	       * spectralEfficiency = log2 (1 + -------------------- )
	       *                                    -ln(5*BER)/1.5
	       * NB: SINR must be expressed in natural unit:
	       * (SINR)dB => 10 ^ (SINR/10)
	       */
		
		double s = Math.log( 1 + (
                Math.pow(10, sinr / 10 )  /
                ( (-Math.log(5.0 * 0.00005 )) / 1.5) )) / Math.log(2);

		int cqi_ = GetCqiFromSpectralEfficiency (s);
		
		System.out.println("Selected CQI: "+cqi_+" for SINR: "+Math.round(sinr));
	}
}
